<template>
  <div class="z-form-upload">
    <div class="z-form-upload__content">
      <template v-if="!disabled">
        <label class="z-form-upload__btn">
          <input
            ref="uploadRef"
            type="file"
            :accept="accept"
            class="z-form-upload__input"
            @change="onInputFielChange"
          />
          <svg-icon name="form-plus"></svg-icon>
          <span>{{ $t('btn') }}</span>
        </label>
        <el-text class="z-form-upload__tip" type="warning" v-if="!hideTip">
          {{
            $t('tip.base', {
              type: fileTypes.join('、'),
              size: props.fileSize > 0 ? $t('tip.size', { size: limitSizeText }) + '，' : '',
              limit: limitLength
            })
          }}
        </el-text>
      </template>

      <div class="z-form-upload__info">
        <template v-for="(item, index) in fileList">
          <t-file-preview
            :type="item.type"
            :src="item.url"
            :disabled="disabled"
            :srcList="imgsList"
            :initial-index="item.index"
            :is-image="item.isImage"
            :file-name="item.fileName"
            :style="previewStyle"
            @close="onClose(index)"
          ></t-file-preview>
        </template>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import tFilePreview from './file-preview.vue'
import { Message } from '@/utils/element'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
const $t = (text: string = '', params: AnyObject = {}) => {
  return t(`component.zform.upload.${text}`, params)
}

const props = withDefaults(
  defineProps<{
    upload?: (file: File) => Promise<string>
    accept?: string
    multiple?: boolean
    value?: any
    limit?: number
    fileTypes?: string[]
    fileSize?: number
    disabled?: boolean
    i18n?: boolean
    i18nPrefix?: string
    baseUrl?: string
    hideTip?: boolean
    previewStyle?: import('vue').StyleValue | string
  }>(),
  {
    accept: 'image/png,image/jpeg',
    fileTypes: () => ['png', 'jpg', 'jpeg'],
    fileSize: 1000
  }
)

const emits = defineEmits<{
  loading: [e: boolean]
  change: [e: string | string[]]
}>()

const urls = defineModel<string | string[]>({
  default: ''
})

const loading = ref(false)

const uploadRef = useTemplateRef('uploadRef')

// 文件列表
const fileList = computed(() => {
  const list = typeof urls.value === 'string' ? (urls.value ? [urls.value] : []) : urls.value || []
  const baseUrl = props.baseUrl || ''
  let index = -1

  const newList = list.map(str => {
    const type =
      // base
      str.indexOf('data:') > -1
        ? str.split(';')[0].split('/')[1]
        : // 截取扩展名
          str.split('.').pop()?.toLowerCase() || ''
    const isImage = ['png', 'jpg', 'jpeg'].includes(type)

    if (isImage) index++
    const url = baseUrl + str
    const fileName = url?.split('/').pop() || ''

    return {
      url,
      type,
      fileName,
      index,
      isImage
    }
  })
  // newList.sort((a, b) => a.type.localeCompare(b.type))
  return newList
})

// 图片列表
const imgsList = computed(() => {
  return fileList.value.filter(it => it.isImage).map(it => it.url)
})

// 计算大小限制
const limitSizeText = computed(() => {
  const size = props.fileSize || 0
  return size >= 1000 ? size / 1000 + 'MB' : size + 'KB'
})

// 计算限制个数
const limitLength = computed(() => {
  return props.multiple && props.limit ? props.limit : 1
})

// 上传
const onInputFielChange = () => {
  const uv = uploadRef.value
  if (!uv?.files?.length) return
  const file = uv?.files[0]

  const limit = props.limit || 1
  // 清空上传信息
  uv.value = ''

  const tmpArr = file.name.split('.')
  const type = (tmpArr.pop() || '').toLowerCase()

  // 判断多选和长度
  if (props.multiple && limit > 0) {
    const len = urls.value?.length || 0
    if (len >= limit) {
      Message($t('tip.limit', { limit }), 'warning')
      return
    }
  }

  // 判断上传文件类型
  if (!props.fileTypes.includes(type)) {
    Message($t('tip.type', { type: props.fileTypes.join('、') }), 'warning')
    return
  }

  // 判断文件大小
  const size = Math.floor(file.size / 1024)
  if (props.fileSize && props.fileSize > 0 && size > props.fileSize) {
    Message($t('tip.size', { size: limitSizeText.value }), 'warning')
    return
  }

  loadingUpdate(true)

  if (props.upload) {
    props.upload(file).then(res => {
      addUrl(res)
      loadingUpdate(false)
    })
  } else {
    const reader = new FileReader()

    reader.onload = e => {
      const result = e.target?.result as string
      addUrl(result)
      loadingUpdate(false)
    }
    reader.readAsDataURL(file)
  }
}

// 加载
const loadingUpdate = (isLoading: boolean) => {
  loading.value = isLoading
  emits('loading', isLoading)
}

// 添加地址
const addUrl = async (str: string) => {
  if (props.multiple) {
    if (!(urls.value instanceof Array)) {
      urls.value = []
    }
    await nextTick()
    urls.value.push(str)
    emits('change', urls.value)
  } else {
    urls.value = str
    emits('change', urls.value)
  }
}

// 删除
const onClose = (index: number) => {
  if (props.multiple && Array.isArray(urls.value)) {
    if (index != void 0 && index >= 0) {
      urls.value.splice(index, 1)
      emits('change', urls.value)
    }
  } else {
    urls.value = ''
    emits('change', urls.value)
  }
}
</script>

<style lang="scss">
.z-form-upload {
  width: 100%;
  &__btn {
    width: 80px;
    color: var(--z-text-color);
    height: 56px;
    cursor: pointer;
    border: 1px dashed var(--z-border-color);
    display: inline-flex;
    font-size: 14px;
    align-items: center;
    flex-direction: column;
    justify-content: center;
    vertical-align: text-bottom;
    background-color: var(--z-fill-color);
    &:hover {
      opacity: 0.85;
    }
    &:active {
      opacity: 0.7;
    }
    span {
      margin-top: 6px;
      line-height: 16px;
    }
  }
  &__input {
    display: none;
    opacity: 0;
    text-indent: -10000px;
  }

  &__tip {
    color: var(--z-text-color-light);
    font-size: 14px;
    margin-top: 8px;
    margin-left: 12px;
  }

  &__info {
    overflow: auto;
    max-height: 217px;
    margin-top: 12px;
    border-radius: var(--z-border-radius);
    background-color: var(--z-fill-color);
  }
}
</style>
